"""
Created on 01-Mar-2010

@author: dexen
"""


import rpyc

from dexen.system.base import service
from dexen.system.base.task import OutputUnit, InputUnit
from dexen.system.server.info import SlaveInfo, MasterInfo
#===============================================================================
# Constants
#===============================================================================
DEAD = "DEAD"
ALIVE = "ALIVE"

INF = 100000
#===============================================================================
# Structural Classes
#===============================================================================
class NodeProxy(object):
    def __init__(self, name, addr, rreg_addr, max_slots=2):
        self.name = name
        self.master_name = None
        self.slaves = []
        self.max_slots = max_slots
        self._state = ALIVE
        self.rreg_addr = rreg_addr
        try:
            self.conn = rpyc.connect(addr[0], addr[1], 
                                     config=service.default_config)
        except EOFError:
            self._state = DEAD
    
    def create_master(self, name):   
        self.conn.root.create_master(name)
        self.master_name = name
        conn = rpyc.connect(self.rreg_addr[0], self.rreg_addr[1])
        addr = conn.root.get_master_addr(self.master_name)
        conn.close() 
        return MasterProxy(name, addr)
    
    def create_slave(self, name):
        self.conn.root.create_slave(name)
        self.slaves.append(name)
        return SlaveInfo(name)
    
    def has_master_slot(self):
        return self.master_name == None

    def dead(self):
        self._state = DEAD

    def is_dead(self):
        return self._state == DEAD
    
    def is_alive(self):
        return self._state == ALIVE
    
    def __repr__(self):
        ret = ""
        for name in self.slaves:
            ret += "    %s\n" %(name)
        for name in self.masters:
            ret += "    %s\n" % (name)
        return ret
        
class MasterProxy(object):
    def __init__(self, name, addr):
        self.name = name
    
        
        self.conn = rpyc.connect(addr[0], addr[1],
                                 config=service.default_config)
        self.info = MasterInfo(name)
        self.slaves_info = None
    
    def run_schema(self, spec):
        try:
            self.conn.root.run_schema(spec)
        except EOFError:
            self.info.crashed()

    def is_crashed(self):
        return self.info.is_crashed()

    def has_pending_task(self):
        return self.info.can_schedule()
    
    def update(self):
        """
        During getting the updated info from master, if the connection is broken
        Notice that the self.info contains the latest info before it crashes
        Note that this proxy object has to turn on the flag crashed in the info
        object
        """
        try:
            info = self.conn.root.get_info()
            """ Note that info is a tuple (master_info, slaves_info) """
            info = rpyc.classic.obtain(info)
            self.info = info[0] # MasterInfo
            self.slaves_info = info[1]
        except EOFError:
            self.info.crashed()  
    
    def schedule(self, name):
        """
            name: Slave name
        """
        try:
            self.conn.root.schedule(name)
        except:
            self.info.crashed()
    
    def get_info(self):
        return self.info

    def get_slaves_info(self):
        return self.slaves_info
    
class SlaveProxy(object):
    def __init__(self, name, spec, addr):
        self.name = name
        self.info = SlaveInfo(name)        
        self.input = None
        self.output = None
        try:
            self.conn = rpyc.connect(addr[0], addr[1],
                                     config=service.default_config)
            self.conn.root.run(spec)
        except EOFError:
            self._crashed()
    
    def is_crashed(self):
        return self.info.is_crashed()
    
    def is_idle(self):
        return self.info.is_idle() 

    def is_executing(self):
        return self.info.is_executing()

    def _crashed(self):
        self.info.crashed()

    def execute(self, input_unit):
        assert isinstance(input_unit, InputUnit)
        try:
            self.input = input_unit 
            self.info.executing()
            print "I am putting", input_unit.task_name, len(input_unit.inds), "into", self.name
            self.conn.root.put(input_unit) # Might need id to match the tasks!
        except EOFError:
            self._crashed()
            
    def update(self):    
        try:
            output = self.conn.root.get()
            self.output = rpyc.classic.obtain(output) # beware output can be none                
            if self.output != None:
                """ 
                Note that the completed means either the script finished success
                fully or crashed
                
                The script crashed is different from slave crashed
                  
                """
                self.info.idle()
        except EOFError:
            self._crashed()
            
    def get_info(self):
        return self.info
    
    def get_output(self):
        return self.output
    
    def get_input(self):
        return self.input
   
